﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using com.susi.util.toolkit;
using JetBrains.Annotations;
#if USING_UNITY
using UnityEngine;
using Random = UnityEngine.Random;
#endif
namespace com.susi.util {
    public static class IEnumableExtensions {
        /// <summary>
        ///     非索引器使用见 <see cref="Enumerable.ElementAtOrDefault{TSource}" />条目
        ///     <para>另注意:ElementAt会调用索引器(例如IList)，此时有可能发生循环调用导致爆栈（当该集合继承了IList且index索引器是不被支持的不良集合），此时可用此API通过遍历器实现</para>
        /// </summary>
        public static T GetByIndex<T>(this IEnumerable<T> ieab, int idx)
        {
            var count = -1;
            foreach (var item in ieab) {
                count++;
                if (count == idx) return item;
            }

            throw new IndexOutOfRangeException();
        }

        public static SortedList<TKEY, TRes> ToSortedList<TKEY, TRes, TSource>(this IEnumerable<TSource> ieab, Func<TSource, TKEY> keySelector, Func<TSource, TRes> valueSelector)
        {
            if (ieab == null || keySelector == null || valueSelector == null) throw new ArgumentNullException();
            var sortlst = new SortedList<TKEY, TRes>();
            foreach (var item in ieab) sortlst.Add(keySelector(item), valueSelector(item));

            return sortlst;
        }

        public static SortedList<TKEY, TRes> ToSortedList<TKEY, TRes, TSource>([NotNull] this IEnumerable<TSource> ieab, [NotNull] Func<TSource, TKEY> keySelector, [NotNull] Func<TSource, TRes> valueSelector, [NotNull] IComparer<TKEY> comparer)
        {
            if (ieab == null || keySelector == null || valueSelector == null || comparer == null) throw new ArgumentNullException();
            var sortlst = new SortedList<TKEY, TRes>(comparer);
            foreach (var item in ieab) sortlst.Add(keySelector(item), valueSelector(item));

            return sortlst;
        }

        public static SortedList<TKEY, TRes> ToSortedList<TKEY, TRes, TSource>([NotNull] this IEnumerable<TSource> ieab, [NotNull] Func<TSource, TKEY> keySelector, [NotNull] Func<TSource, TRes> valueSelector, [NotNull] Func<TKEY, TKEY, int> comparer)
        {
            return ToSortedList(ieab, keySelector, valueSelector, new IComparerAdapter<TKEY>(comparer));
        }

        public static SortedList<TKey, TSource> ToSortedList<TKey, TSource>(this IEnumerable<TSource> ieab, Func<TSource, TKey> keySelector)
        {
            if (ieab == null || keySelector == null) throw new ArgumentNullException();
            var sortlst = new SortedList<TKey, TSource>();
            foreach (var item in ieab) sortlst.Add(keySelector(item), item);

            return sortlst;
        }

        /// <summary>
        ///     通过类型强制转换返回新的遍历体
        /// <para>use <see cref="Enumerable.Select{TSource,TResult}(System.Collections.Generic.IEnumerable{TSource},System.Func{TSource,TResult})" /> instead</para> 
        /// <para>use <see cref="System.Linq.Enumerable.Cast{TResult}"/> instead</para>
        /// <para>use <see cref="Enumerable.OfType{TResult}"/> instead</para>
        /// </summary>
        [Obsolete("See Select/Cast/OfType")]
        public static IEnumerable<T> ParseEnumerable<A, T>(this IEnumerable<A> orginal) where T : A
        {
            using (var itor = orginal.GetEnumerator())
                while (itor.MoveNext())
                    yield return (T) itor.Current;
        }

        /// <summary>
        ///     (弃用)通过强制类型转换转换让编译器将其视为不是泛型实现。等同于'(IEnumerable)source'
        /// </summary>
        [Obsolete("useless")]
        public static IEnumerable ParseEnumerable<T>(this IEnumerable<T> source)
        {
            return source;
        }

        /// <summary>
        ///     返回一个新定义的可遍历体，不会马上转换
        ///<para>已弃用，见<see cref="Enumerable.Select{TSource,TResult}(System.Collections.Generic.IEnumerable{TSource},System.Func{TSource,TResult})" /></para>
        /// </summary>
        [Obsolete("see Select")]
        public static IEnumerable<T> ParseEnumerable<A, T>(this IEnumerable<A> orginal, Func<A, T> AtoT)
        {
            using (var itor = orginal.GetEnumerator())
                while (itor.MoveNext())
                    yield return AtoT(itor.Current);
        }

        /// <summary>
        ///     通过强制类型转换转换非泛型到泛型，可能报错
        /// <para>use <see cref="System.Linq.Enumerable.Cast{TResult}"/> instead</para>
        /// <para>use <see cref="Enumerable.OfType{TResult}"/> instead</para>
        /// </summary>
        [Obsolete("See Cast/OfType")]
        public static IEnumerable<T> ParseEnumerable<T>(this IEnumerable ieab)
        {
            if (ieab == null) yield break;
            var itor = ieab.GetEnumerator();
            while (itor.MoveNext()) yield return (T) itor.Current;

            itor.Reset();
        }

        /// <summary>
        ///     比系统自带的<see cref="System.Linq.Enumerable.AsEnumerable{T}" />>多一层判空，保证不返回null
        ///     也可用于对可能为null的遍历体使用以保证不会获得null
        /// <para>对于需要延迟检查的情况（远程数据库），要用<see cref="System.Linq.Enumerable.DefaultIfEmpty{TSource}(IEnumerable{TSource}, TSource) "/>，它会抛null异常</para>
        /// </summary>
        [NotNull]
        public static IEnumerable<T> AsIEnumableOrEmpty<T>(this IEnumerable<T> ieab)
        {
            return ieab ?? Enumerable.Empty<T>();
        }

        /// <summary>
        ///     显式引出接口方法
        /// <para>当继承了IEnumerable{T}后IDE默认隐藏IEnumerable，这个可以显式引出，并且可以返回Empty</para>
        /// </summary>
        [NotNull]
        public static IEnumerable AsIEnumable(this IEnumerable ieab)
        {
            return ieab ?? Enumerable.Empty<object>();
        }


        /// <summary>
        ///     将遍历体包装为<see cref="CollectionAdapter{T}" />，不触发遍历
        /// 与Select相比较，少一个委托调用和少一个类型依赖
        /// </summary>
        public static ICollection<T> WarpAsICollection<T>(this IEnumerable<T> ts)
        {
            return new CollectionAdapter<T>(ts);
        }

        /// <summary>
        /// A集合是否完全包含B集合
        /// 集合相等的情况也返回true
        /// <para>根据Except的实现，会立即遍历源集合source。source如果是列表，会坍缩成集合。another不会坍缩，仍会全部遍历</para>
        /// </summary>
        public static bool IsContainsSet<T>([NotNull] this IEnumerable<T> source, [NotNull] IEnumerable<T> another)
        {
            return another.Except(source).IsLessThan(1);
        }

        public static void CopyTo<T>(this IEnumerable<T> ts, T[] array, int arrayIndex)
        {
            if (ts == null || array == null) throw new ArgumentNullException();
            if (arrayIndex >= array.Length) throw new ArgumentOutOfRangeException();
            using (var itor = ts.GetEnumerator()) {
                var len = array.Length;

                for (; arrayIndex < len && itor.MoveNext(); arrayIndex++) array[arrayIndex] = itor.Current;
            }
        }

        /// <summary>
        ///     比较集合大小是否比coutn大。相对于linq.count()来说这个API不会遍历全集，只数前几集
        ///     <para>null元素视为有效</para>
        ///     <para>null参数会抛<see cref="ArgumentNullException" /></para>
        /// </summary>
        public static bool IsMoreThan<T>(this IEnumerable<T> ienum, int count)
        {
            using (var itor = ienum.GetEnumerator()) {
                while (itor.MoveNext()) {
                    count--;
                    if (count < 0) return true;
                }
            }

            return false;
        }

        /// <summary>
        ///     比较集合大小是否比coutn大。相对于linq.count()来说这个API不会遍历全集，只数前几集
        ///     <para>null元素视为有效</para>
        ///     <para>null参数会抛<see cref="ArgumentNullException" /></para>
        /// </summary>
        public static bool IsMoreThan<T>(this T[] ienum, int count)
        {
            return ienum.Length > count;
        }

        /// <summary>
        ///     比较集合大小是否比coutn大或等于。相对于linq.count()来说这个API不会遍历全集，只数前几集
        ///     <para>null元素视为有效</para>
        ///     <para>null参数会抛<see cref="ArgumentNullException" /></para>
        /// </summary>
        public static bool IsMoreThanOrEqual<T>(this IEnumerable<T> ienum, int count)
        {
            using (var itor = ienum.GetEnumerator()) {
                while (itor.MoveNext()) {
                    count--;
                    if (count <= 0) return true;
                }
            }

            return false;
        }

        /// <summary>
        ///     比较集合大小是否比coutn大或等于。相对于linq.count()来说这个API不会遍历全集，只数前几集
        ///     <para>null元素视为有效</para>
        ///     <para>null参数会抛<see cref="ArgumentNullException" /></para>
        /// </summary>
        public static bool IsMoreThanOrEqual<T>(this T[] ienum, int count)
        {
            return ienum.Length >= count;
        }

        /// <summary>
        ///     比较集合大小是否比coutn小。相对于linq.count()来说这个API可能不会遍历全集，只数前几集
        ///     <para>null元素视为有效</para>
        ///     <para>null参数会抛<see cref="ArgumentNullException" /></para>
        /// </summary>
        public static bool IsLessThan<T>(this IEnumerable<T> ienum, int count)
        {
            return !IsMoreThanOrEqual(ienum, count);
        }

        /// <summary>
        ///     比较集合大小是否比coutn小。相对于linq.count()来说这个API可能不会遍历全集，只数前几集
        ///     <para>null元素视为有效</para>
        ///     <para>null参数会抛<see cref="ArgumentNullException" /></para>
        /// </summary>
        public static bool IsLessThan<T>(this T[] ienum, int count)
        {
            return ienum.Length < count;
        }

        /// <summary>
        ///     比较集合大小是否比coutn小或等于。相对于linq.count()来说这个API可能不会遍历全集，只数前几集
        ///     <para>null元素视为有效</para>
        ///     <para>null参数会抛<see cref="ArgumentNullException" /></para>
        /// </summary>
        public static bool IsLessThanOrEqual<T>(this IEnumerable<T> ienum, int count)
        {
            return !IsMoreThan(ienum, count);
        }

        public static bool IsEmpty<T>(this IEnumerable<T> enumerable) => !IsMoreThan(enumerable, 0);

        /// <summary>
        ///     比较集合大小是否比coutn小或等于。相对于linq.count()来说这个API可能不会遍历全集，只数前几集
        ///     <para>null元素视为有效</para>
        ///     <para>null参数会抛<see cref="ArgumentNullException" /></para>
        /// </summary>
        public static bool IsLessThanOrEqual<T>(this T[] ienum, int count)
        {
            return ienum.Length <= count;
        }

        public static IEnumerable<T> Except<T>(this IEnumerable<T> enumerable, T item)
        {
            return enumerable.Except(new T[] {item});
        }

        public static IEnumerable<T> Except<T>(this IEnumerable<T> enumerable, T item, IEqualityComparer<T> equality)
        {
            return enumerable.Except(new T[] {item}, equality);
        }

        public static IOrderedEnumerable<T> OrderBy<K, T>(this IEnumerable<T> enumerable, Func<T, K> keyselector, Func<K, K, int> comparer)
        {
            return enumerable.OrderBy(keyselector, new ComparerAdapter<K>(comparer));
        }

        /// <summary>
        ///  通过指定comparerkeyselector进行排序
        /// </summary>
        public static IOrderedEnumerable<TSource> OrderBy
            <TSource, TCompareKey>(this IEnumerable<TSource> sources, Func<TSource, TCompareKey> comparerkeyselector)
            where TCompareKey : IComparer<TCompareKey>
        {
            var compare = new ComparerAdapter<TSource>(
                (a, b) =>
                {
                    var akey = comparerkeyselector(a);
                    var bkey = comparerkeyselector(b);
                    return akey.Compare(akey, bkey);
                });
            return sources.OrderBy(v => v, compare);
        }

        /// <summary>
        /// 等概率抽取1个元素
        ///  collection必须可数
        /// </summary>
        public static T Draw<T>([NotNull] this IEnumerable<T> collection)
        {
            return Draw(collection.ToArray());
        }
        
        /// <summary>
        /// 等概率抽取1个元素
        ///  collection必须可数
        /// </summary>
        public static T Draw<T>([NotNull] this T[] collection)
        {
            var randomindex = 0;
#if USING_UNITY
            randomindex = UnityEngine.Random.Range(0, collection.Length - 1);
#else
            randomindex = new System.Random().Next(collection.Length-1);
#endif
            return collection[randomindex];
        }

        /// <summary>
        /// 加权抽取1个元素
        /// 当源集为空的时候，返回default
        /// </summary>
        [CanBeNull]
        public static T Draw<T>([NotNull] this IEnumerable<T> collection, [NotNull] Func<T,int> weightGetter)
        {
            return Draw(collection.ToArray(), weightGetter);
        }
        
        /// <summary>
        /// 加权抽取1个元素
        /// 当源集为空的时候，返回default
        /// </summary>
        [CanBeNull]
        public static T Draw<T>([NotNull] this IList<T> collection, [NotNull] Func<T,int> weightGetter)
        {
            var ary = collection;
            var sum = ary.Sum(weightGetter);
#if USING_UNITY
            var rand = UnityEngine.Random.Range(0, sum);
#else
            var rand = new System.Random().Next(sum);
#endif
            foreach (var item in ary) {
                rand -= weightGetter(item);
                if (rand < 0) return item;
            }

            return default; //此时，collection是Empty集合
        }
        
        /// <summary>
        /// 加权抽取1个元素
        /// 当源集为空的时候，返回default
        /// </summary>
        [CanBeNull]
        public static T Draw<T>([NotNull] this T[] collection, [NotNull] Func<T,int> weightGetter)
        {
            var ary = collection;
            var sum = ary.Sum(weightGetter);
#if USING_UNITY
            var rand = UnityEngine.Random.Range(0, sum);
#else
            var rand = new System.Random().Next(sum);
#endif
            foreach (var item in ary) {
                rand -= weightGetter(item);
                if (rand < 0) return item;
            }

            return default; //此时，collection是Empty集合
        }

        /// <summary>
        /// 等概率抽取n个元素（排列）
        /// </summary>
        [CanBeNull]
        public static IEnumerable<T> Permutation<T>([NotNull] this IEnumerable<T> collection, int count)
        {
            return count == 1 ? new[] {collection.Draw()} : collection.OrderBy(v=>Guid.NewGuid()).Take(count);
        }
        
        /// <summary>
        /// 等概率抽取n个元素（排列）
        /// </summary>
        [NotNull]
        public static IEnumerable<T> Permutation<T>([NotNull] this T[] collection, int count)
        {
            if (collection == null) throw new ArgumentNullException(nameof(collection));
            if (collection.Length < count || count<=0 ) throw new ArgumentOutOfRangeException(nameof(count));
            return count == 1 ? new[] {collection.Draw()} : collection.OrderBy(v=>Guid.NewGuid()).Take(count);
        }
        
        /// <summary>
        ///  等概率抽取抽取n个元素（组合）
        /// </summary>
        [CanBeNull]
        public static IEnumerable<T> Combination<T>([NotNull] this IEnumerable<T> collection, int count)
        {
            return collection.ToArray().Combination(count);
        }
        
        /// <summary>
        ///  等概率抽取抽取n个元素（组合）
        /// </summary>
        [CanBeNull]
        public static IEnumerable<T> Combination<T>([NotNull] this T[] collection, int count)
        {
            for (; count > 0; count--) {
                yield return collection.Draw();
            }
        }
        
        /// <summary>
        /// 加权抽取n个元素（组合）(非树）
        /// </summary>
        [CanBeNull]
        public static IEnumerable<T> Combination<T>([NotNull] this (int weight, T item)[] weight_item, int count)
        {
            var sum = weight_item.Sum(v => v.Item1);
            for (; count > 0; count--) {
#if USING_UNITY
                var rand = Random.Range(1, sum);
#else
                var rand = new Random().Next(1, sum);
#endif
                foreach (var (weight, item) in weight_item) {
                    rand -= weight;
                    if (rand <= 0) {
                        yield return item;
                    }
                }
            }
        }
        
        
        /// <summary>
        /// 加权抽取n个元素（排列）(非树）
        /// </summary>
        [CanBeNull]
        public static IEnumerable<T> Permutation<T>([NotNull] this IEnumerable<(int weight, T item)> weight_item, int count)
        {
            var copy =  weight_item.ToList();
            var sum  = copy.Sum(v => v.Item1);
            for (; count > 0; count--) {
#if USING_UNITY
                var rand = Random.Range(1, sum);
#else
                var rand = new Random().Next(1, sum);
#endif
                for (var i = 0; i < copy.Count; i++) {
                    rand -= copy[i].weight;
                    if (rand <= 0) {
                        yield return copy[i].item;
                        sum -= copy[i].weight;
                        copy.RemoveAt(i);
                    }
                }
            }
        }

        /// <summary>
        /// 选取所有里表元素，等同SelectMany(v => v)
        /// </summary>
        public static IEnumerable<T> SelectMany<T>(this IEnumerable<IEnumerable<T>> source)
        {
            foreach (var item in source) {
                foreach (var initem in item) {
                    yield return initem;
                }
            }
        }
    }
}